﻿using System;
using System.Collections.Generic;
using System.IO;
using Eventd.Socket;
using RazorEngine;
using RazorEngine.Configuration;
using RazorEngine.Templating;

namespace Shot.Presentation.UI
{
    /// <summary>
    /// A ViewPresenter is capable of presenting a Razor template to the user for viewing. ViewPresenters act as the foundational
    /// starting point for any kind of user interface in a Shot application. Controls are also subclasses of ViewPresenter, giving
    /// them the ability to maintain a user interface with backing code.
    /// 
    /// By default, a ViewPresenter will try to load a template from the "Templates" folder in the project. The ViewPresenter will
    /// search for a template that matches the name of the ViewPresenter subclass, followed by the ".cshtml" extension.
    /// </summary>
    public abstract class ViewPresenter : Presenter
    {
        #region Properties

        /// <summary>
        /// Parent view presenter
        /// </summary>
        public ViewPresenter Parent { get; set; }

        /// <summary>
        /// Containing element
        /// </summary>
        public Element Container { get; set; }

        /// <summary>
        /// Razor template or template file path.
        /// </summary>
        public String Template { get; set; }

        /// <summary>
        /// List of events and corresponding event GUIDs
        /// </summary>
        public Dictionary<string, string> Events { get; set; }

        /// <summary>
        /// List of paths for template searching
        /// </summary>
        public string[] TemplatePaths { get; set; }

        #endregion

        #region Presentation Overrides

        /// <summary>
        /// Construct the ViewPresenter and set up a default template file.
        /// </summary>
        protected ViewPresenter()
        {
            Events = new Dictionary<string, string>();
            Template = "Templates/" + GetType().Name + ".cshtml";
            TemplatePaths = SetupDefaultTemplatePaths();
        }

        /// <summary>
        /// Render the template into the containing element
        /// </summary>
        public override void Present()
        {
            base.Present();

            var renderedTemplate = RenderTemplate();
            if (Container == null) Container = Instance.Element("body");
            Container.Html = renderedTemplate;
        }

        /// <summary>
        /// Refreshes the view by re-presenting it
        /// </summary>
        public void Refresh()
        {
            Instance.App.Present(this, Instance);
        }

        #endregion

        /// <summary>
        /// Present the view presenter as a control of a parent view presenter.
        /// </summary>
        /// <param name="parentPresenter"></param>
        /// <param name="container"></param>
        public void PresentAsControl(ViewPresenter parentPresenter, Element container)
        {
            Instance = parentPresenter.Instance;
            Parent = parentPresenter;
            Container = container;

            Instance.App.Present(this, Instance);
        }

        /// <summary>
        /// Allocate a new Element with a randomized selector for non-conflicting use purposes.
        /// </summary>
        /// <returns></returns>
        public Element AllocateElement()
        {
            var uuid = Guid.NewGuid().ToString();
            return Instance.Element("#" + uuid);
        }

        /// <summary>
        /// Add an event listener for a synonym event
        /// </summary>
        /// <param name="eventName"></param>
        /// <param name="callback"></param>
        public void On(string eventName, Action<dynamic> callback)
        {
            if (Events.ContainsKey(eventName))
            {
                Instance.On(Events[eventName], callback);
            }
        }

        #region Private Methods

        private string RenderTemplate()
        {
            var config = new TemplateServiceConfiguration
            {
                BaseTemplateType = typeof(ViewPresenterTemplateBase<>)
            };

            Razor.SetTemplateService(new TemplateService(config));
            return Razor.Parse(GetTemplateContents(), this);
        }

        private string GetTemplateContents()
        {
            foreach (var templatePath in TemplatePaths)
            {
                var formattedTemplatePath = string.Format(templatePath, Template);

                if (File.Exists(formattedTemplatePath))
                {
                    return File.ReadAllText(formattedTemplatePath);
                }
            }

            return Template;
        }

        private string[] SetupDefaultTemplatePaths()
        {
            // ReSharper disable PossibleNullReferenceException
            var projectPath = Directory.GetParent(Directory.GetCurrentDirectory()).Parent.FullName;
            // ReSharper restore PossibleNullReferenceException

            return new[]
            {
                Environment.CurrentDirectory + "/Templates/{0}.mustache",
                Environment.CurrentDirectory + "/{0}",
                projectPath + "/Templates/{0}.mustache",
                projectPath + "/{0}",
                "{0}"
            };
        }

        #endregion
    }
}
